﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using nameero.util;
using System.IO;

namespace nameero
{
    class Program
    {
        private static FileStream file;
        private static StreamWriter writer;
        private static GameState currentState;
        private static Boolean player1turn;
        static int potez = 1;

        // udri mile 
        static void Main(string[] args)
        {
            file = new FileStream("ispis.txt", FileMode.OpenOrCreate, FileAccess.Write);
            writer = new StreamWriter(file);
            currentState = new GameState();
            player1turn = true;
            PrintGameState();

            while (true)
            {
                Player1Move(currentState);
                PrintGameState();

                Player2Move(currentState);
                PrintGameState();
            }
        }

        // printni plocu - ekran
        private static void PrintGameState()
        {
            Console.WriteLine("\n\nPotez #{0}", potez);
            Console.WriteLine("========");
            if (!player1turn) { Console.WriteLine("Player2| <--"); }
            else { Console.WriteLine("Player2|"); }
            Console.WriteLine("================================");
            System.Console.Out.Write(currentState.ToString());
            Console.WriteLine("================================");
            if (player1turn) { Console.WriteLine("Player1| <--"); }
            else { Console.WriteLine("Player1|"); }
            Console.WriteLine("========\n");

            PrintIntoFile();
        }

        // ispis partije u datoteku
        private static void PrintIntoFile()
        {
            writer.WriteLine("\n\nPotez #{0}", potez);
            writer.WriteLine("========");
            if (!player1turn) { writer.WriteLine("Player2| <--"); }
            else { writer.WriteLine("Player2|"); }
            writer.WriteLine("================================");
            writer.Write(currentState.ToString());
            writer.WriteLine("================================");
            if (player1turn) { writer.WriteLine("Player1| <--"); }
            else { writer.WriteLine("Player1|"); }
            writer.WriteLine("========\n");
        }

        // igrac 1 - kruzic, donji
        private static void Player1Move(GameState gs)
        {
            potez++;
            HumanMove(gs);
            player1turn = false;
        }

        // igrac 2 - krizic, gornji
        private static void Player2Move(GameState gs)
        {
            potez++;
            ComputerMove(gs);
            player1turn = true;
        }

        // potez human beinga
        private static void HumanMove(GameState gs)
        {

            LinkedList<MultiMove> jumps = gs.jumpAround();
            MultiMove selectedMove = null;

            // ima li skokova?
            if (jumps.Count > 0)
            {
                for (int i = 0; i < jumps.Count; i++)
                    Console.WriteLine((i + 1) + ") " + jumps.ElementAt(i).ToString());

                // odabir poteza
                System.Console.Out.Write("Unesite index zeljenog poteza: ");
                int index;
                while (true)
                {
                    String input = System.Console.ReadLine();
                    try
                    {
                        index = int.Parse(input);
                        if (index < 0 || index > jumps.Count) throw new Exception();
                        else break;
                    }
                    catch (Exception e)
                    {
                        System.Console.Out.Write("Nemoguc potez! Birajte ponovo:");
                    }
                }

                // obavi odabrani potez
                selectedMove = jumps.ElementAt(index - 1);
                DoSelectedMove(gs, selectedMove);
            }

            // ako nema skokova, izlistaj prijelaze
            else
            {
                LinkedList<GameState> moves = gs.getAllMoves(player1turn);
                // ako nema poteza - game over
                if (moves.Count == 0)
                    GameOver();

                for (int i = 0; i < moves.Count; i++)
                    Console.WriteLine((i + 1) + ") " + moves.ElementAt(i).GetMovIn());

                // odabir poteza
                System.Console.Out.Write("Unesite index zeljenog poteza: ");
                int index;
                while (true)
                {
                    String input = System.Console.ReadLine();
                    try
                    {
                        index = int.Parse(input);
                        if (index < 0 || index > moves.Count) throw new Exception();
                        else break;
                    }
                    catch (Exception e)
                    {
                        System.Console.Out.Write("Nemoguc potez! Birajte ponovo:");
                    }
                }

                GameState selectedState = moves.ElementAt(index - 1);
                currentState = currentState = new GameState(gs.doMove(selectedState.GetStartPoint(), selectedState.GetEndPoint(), player1turn));
            }

        }

        // potez racunala
        private static void ComputerMove(GameState gs)
        {
            LinkedList<MultiMove> jumps = gs.jumpAround();
            MultiMove selectedMove = null;

            // ima li skokova?
            if (jumps.Count > 0)
            {
                Console.WriteLine("ima skokova - " + player1turn + ":");
                // ispis mogucih poteza racunala
                Console.WriteLine("Moguci potezi racunala - " + player1turn + ":");
                for (int i = 0; i < jumps.Count; i++)
                    Console.WriteLine((i + 1) + ") " + jumps.ElementAt(i).ToString());

                if (jumps.Count == 1)
                    selectedMove = jumps.ElementAt(0);
                else
                    selectedMove = CallAlphaBetaJump(gs);

                DoSelectedMove(gs, selectedMove);
                Console.WriteLine("komp obavio: " + selectedMove.ToString());
            }

            // ako nema skokova, izlistaj prijelaze
            else
            {
                LinkedList<GameState> moves = gs.getAllMoves(player1turn);
                // ako nema poteza - game over
                if (moves.Count == 0)
                    GameOver();

                // ispis mogucih poteza racunala
                Console.WriteLine("Moguci potezi racunala - " + player1turn + ":");
                for (int i = 0; i < moves.Count; i++)
                    Console.WriteLine((i + 1) + ") " + moves.ElementAt(i).GetMovIn());

                // ako je samo jedan, odmah obavi
                if (moves.Count == 1)
                    currentState = new GameState(gs.doMove(moves.ElementAt(0).GetStartPoint(), moves.ElementAt(0).GetEndPoint(), player1turn));
                else
                    currentState = new GameState(CallAlphaBetaMove(gs));

                Console.WriteLine("komp obavio: " + currentState.GetMovIn());
            }
        }

        private static void DoSelectedMove(GameState gs, MultiMove selectedMove)
        {
            LinkedList<Point> points = selectedMove.GetMovePoints();
            if (points.Count == 2)
                currentState = new GameState(gs.doMove(points.ElementAt(0), points.ElementAt(1), player1turn));
            else
                DoMultiJump(points, gs);
        }

        private static GameState CallAlphaBetaMove(GameState gs)
        {
            Node root = new Node();
            root.setGameState(gs);

            Console.WriteLine("djeca prije " + root.getChildren().Count);
            AlphaBetaMinimax.alphaBetaMiniMax(root, player1turn, Int32.MinValue, Int32.MaxValue);
            Console.WriteLine("djeca poslije " + root.getChildren().Count);

            List<Node> children = root.getChildren();

            // ako nema više poteza - gotova igra
            if (children.Count == 0)
            {
                Console.WriteLine("Game Over!" + !player1turn + " wins! (True = p1, False = p2)");
                Console.Read();
                writer.Close();
                System.Environment.Exit(1);
            }

            GameState selectedGState = null;
            // inače odaberi sljedeći potez
            Console.WriteLine("root alfabeta= " + root.getAlphaBetaValue());
            foreach (Node n in children)
            {
                Console.WriteLine("dete alfabeta= " + n.getAlphaBetaValue());
                if (n.getAlphaBetaValue() == root.getAlphaBetaValue())
                {
                    Console.WriteLine("alfabeta isti = " + root.getAlphaBetaValue());
                    selectedGState = n.getGameState();
                    break;
                }
            }
            return selectedGState;
        }

        private static MultiMove CallAlphaBetaJump(GameState gs)
        {
            Node root = new Node();
            root.setGameState(gs);

            Console.WriteLine("djeca prije " + root.getChildren().Count);
            AlphaBetaMinimax.alphaBetaMiniMax(root, player1turn, Int32.MinValue, Int32.MaxValue);
            Console.WriteLine("djeca poslije " + root.getChildren().Count);

            List<Node> children = root.getChildren();

            // ako nema više poteza - gotova igra
            if (children.Count == 0)
            {
                Console.WriteLine("Game Over!" + !player1turn + " wins! (True = p1, False = p2)");
                Console.Read();
                writer.Close();
                System.Environment.Exit(1);
            }

            Node current = root;
            MultiMove jumpPath = new MultiMove();

            // inače odaberi sljedeći potez
            Console.WriteLine("root alfabeta= " + root.getAlphaBetaValue());
            jumpPath.AddMovePoint(current.getGameState().GetStartPoint());
            while (current.getGameState().jumpAround().Count > 0)
            {
                foreach (Node n in children)
                {
                    if (n.getAlphaBetaValue() == current.getAlphaBetaValue())
                    {
                        //TODO: provjeriti jel nebi trebalo stvarno biti next
                        current = n;
                        jumpPath.AddMovePoint(current.getGameState().GetEndPoint());
                        children = current.getChildren();
                    }
                }
            }
            return jumpPath;
        }

        // obavi multiJump
        private static void DoMultiJump(LinkedList<Point> points, GameState gs)
        {
            for (int i = 0; i < points.Count - 1; ++i)
            {
                currentState = new GameState(gs.doMove(points.ElementAt(i), points.ElementAt(i + 1), player1turn));
                gs = currentState;
            }
        }

        private static void GameOver()
        {
            Console.WriteLine("Game Over!" + !player1turn + " wins! (True = p1, False = p2)");
            writer.WriteLine("Game Over!" + !player1turn + " wins! (True = p1, False = p2)");
            Console.Read();
            writer.Close();
            System.Environment.Exit(1);
        }
    }
}